package labox.innovation.gameserver.ai;

import static labox.innovation.gameserver.ai.CtrlIntention.*;

import java.util.Collection;

import javolution.util.FastMap;
import javolution.util.FastMap.Entry;
import labox.innovation.config.Config;
import labox.innovation.gameserver.GameTimeController;
import labox.innovation.gameserver.model.L2CharPosition;
import labox.innovation.gameserver.model.L2Object;
import labox.innovation.gameserver.model.L2Skill;
import labox.innovation.gameserver.model.L2Spawn;
import labox.innovation.gameserver.model.Scene;
import labox.innovation.gameserver.model.actor.L2Attackable;
import labox.innovation.gameserver.model.actor.L2Character;
import labox.innovation.gameserver.model.actor.instance.FuzeGuardInstance;
import labox.innovation.gameserver.model.actor.instance.L2MinionInstance;
import labox.innovation.gameserver.model.actor.instance.FuzeMonsterInstance;
import labox.innovation.gameserver.templates.chars.NpcTemplate.AIType;
import labox.innovation.gameserver.util.Util;
import labox.innovation.util.Rnd;

/**
 * This class manages AI of L2Attackable.<BR>
 * <BR>
 * 
 */
public class L2AttackableAI extends L2CharacterAI {

	/**
	 * 随机行走的发生概率
	 */
	private static final int RANDOM_WALK_RATE = 150;

	/**
	 * 施法buff的间隔
	 */
	private static final int CAST_BUFF_INTERVAL = 100;

	/**
	 * 施法的间隔
	 */
	private static final int CAST_SKILL_INTERVAL = 60;

	/**
	 * 攻击超时时间(10s)
	 */
	private static final int MAX_ATTACK_TIMEOUT = 100;

	/**
	 * 全局仇恨触发间隔(3s)
	 */
	private static final int GLOBA_AGGRO_INTERVAL = 30;

	/**
	 * 下次攻击超时的时间
	 */
	private int _attackTimeout;

	/**
	 * 为攻击AI准备的分析怪物和他的目标的对象
	 */
	private SelfAnalysis _selfAnalysis = new SelfAnalysis();

	/**
	 * 全局仇恨检查触发计数器
	 */
	private int _globalAggro;

	/**
	 * L2AttackableAI的构造函数.<BR>
	 * <BR>
	 * 
	 * @param accessor
	 *            The AI accessor of the L2Character
	 * 
	 */
	public L2AttackableAI(L2Character.AIAccessor accessor) {
		super(accessor);
		_selfAnalysis.init();
	}

	/**
	 * 
	 * 如果目标是能被自动攻击的（取决于角色类型）返回True.<BR>
	 * <BR>
	 * 
	 * <B><U> Actor is a L2GuardInstance</U> :</B><BR>
	 * <BR>
	 * <li>The target isn't a Folk or a Door</li> <li>The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)</li> <li>The target is in the actor Aggro range and is at the same height</li> <li>The L2PcInstance target has karma (=PK)</li> <li>The L2MonsterInstance target is aggressive</li><BR>
	 * <BR>
	 * 
	 * <B><U> Actor is a L2SiegeGuardInstance</U> :</B><BR>
	 * <BR>
	 * <li>The target isn't a Folk or a Door</li> <li>The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)</li> <li>The target is in the actor Aggro range and is at the same height</li> <li>A siege is in progress</li> <li>The L2PcInstance target isn't a Defender</li><BR>
	 * <BR>
	 * 
	 * <B><U> Actor is a L2FriendlyMobInstance</U> :</B><BR>
	 * <BR>
	 * <li>The target isn't a Folk, a Door or another L2NpcInstance</li> <li>The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)</li> <li>The target is in the actor Aggro range and is at the same height</li> <li>The L2PcInstance target has karma (=PK)</li><BR>
	 * <BR>
	 * 
	 * <B><U> Actor is a L2MonsterInstance</U> :</B><BR>
	 * <BR>
	 * <li>The target isn't a Folk, a Door or another L2NpcInstance</li> <li>The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)</li> <li>The target is in the actor Aggro range and is at the same height</li> <li>The actor is Aggressive</li><BR>
	 * <BR>
	 * 
	 * @param target
	 *            The targeted L2Object
	 * 
	 */
	private boolean autoAttackCondition(L2Character target) {
		if (target == null)
			return false;

		L2Attackable me = (L2Attackable) _actor;

		// 检查角色是不是死亡了，是不是在仇恨范围内,是否处于无敌状态
		if (target.isDead() || target.isInvul() || !me.isInsideRadius(target, me.getAggroRange()))
			return false;

		if (!me.isRaid() && target.isSilentMoving()) {// 如果AI不是团战boss并且目标处于静行术状态
			return false;
		}

		//首先检查基础的攻击行为能否发生
		if (me.isAutoAttackable(target)) {
			return true;//GeoData.getInstance().canSeeTarget(me, target);//检查是否在视野范围内
		}
		return false;
	}

	/**
	 * 
	 * 设置这一L2CharacterAI的意图，并创建一个AI任务每1秒调度一次（就是呼叫onEvtThink方法）。
	 * 
	 * <FONT COLOR=#FF0000><B> <U>Caution</U> : If actor _knowPlayer isn't EMPTY, AI_INTENTION_IDLE will be change in AI_INTENTION_ACTIVE</B></FONT><BR>
	 * <BR>
	 * 
	 * @param intention
	 *            The new Intention to set to the AI
	 * @param arg0
	 *            The first parameter of the Intention
	 * @param arg1
	 *            The second parameter of the Intention
	 * 
	 */
	@Override
	void changeIntention(CtrlIntention intention, Object arg0, Object arg1) {
		if (intention == AI_INTENTION_IDLE || intention == AI_INTENTION_ACTIVE) {
			// 如果角色没有死亡
			if (!_actor.isDead()) {
				L2Attackable mob = (L2Attackable) _actor;

				if (mob.getKnownList().getKnownPlayers().isEmpty()) {
					L2Spawn spawn = mob.getSpawn();
					if (spawn != null) {//如果离出生点太远了，准备返回
						if (Util.FastDistance2D(spawn.getLocx(), spawn.getLocy(), mob.getX(), mob.getY()) > Config.MAX_DRIFT_RANGE)
							intention = AI_INTENTION_ACTIVE;
					}

				} else {// 如果他的已知列表中玩家数量不是空的设置意图为 AI_INTENTION_ACTIVE
					intention = AI_INTENTION_ACTIVE;
				}
			}

			if (intention == AI_INTENTION_IDLE) {
				// Set the Intention of this L2AttackableAI to AI_INTENTION_IDLE
				super.changeIntention(AI_INTENTION_IDLE, null, null);
				return;
			}
		}

		// Set the Intention of this L2AttackableAI to intention
		super.changeIntention(intention, arg0, arg1);
	}

	/**
	 * 管理攻击的意图：如有必要停止当前的攻击，计算攻击的超时。开始一个新的攻击，并调度Think Event.<BR>
	 * <BR>
	 * 
	 * @param target
	 *            The L2Character to attack
	 * 
	 */
	@Override
	protected void onIntentionAttack(L2Character target) {
		int cur_tick = GameTimeController.getGameTicks();

		//计算新的攻击超时
		_attackTimeout = MAX_ATTACK_TIMEOUT + cur_tick;

		// 自我分析给自己加buff
		if (_selfAnalysis.lastBuffTick + CAST_BUFF_INTERVAL < cur_tick) {
			// forcing long reuse delay so if cast get interrupted or there would be several buffs, doesn't cast again
			_selfAnalysis.lastBuffTick = cur_tick;

			for (L2Skill sk : _selfAnalysis.buffSkills) {
				if (_actor.getFirstEffect(sk.getId()) == null) {
					if (_actor.getCurrentMp() < sk.getMpConsume())
						continue;
					if (_actor.isSkillDisabled(sk.getId()))
						continue;
					L2Object OldTarget = _actor.getTarget();
					_actor.setTarget(_actor);
					_accessor.doCast(sk);
					_actor.setTarget(OldTarget);
				}
			}
		}
		// Manage the Attack Intention : Stop current Attack (if necessary), Start a new Attack and Launch Think Event
		super.onIntentionAttack(target);
	}

	/**
	 * Manage AI standard thinks of a L2Attackable (called by onEvtThink).<BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Update every 1s the _globalAggro counter to come close to 0</li> <li>If the actor is Aggressive and can attack, add all autoAttackable L2Character in its Aggro Range to its _aggroList, chose a target and order to attack it</li> <li>If the actor is a L2GuardInstance that can't attack, order to it to return to its home location</li> <li>If the actor is a L2MonsterInstance that can't attack, order to it to random walk (1/100)</li><BR>
	 * <BR>
	 * 
	 */
	private void thinkActive() {
		L2Character hater = thinkAttackable();
		if (hater != null) {
			setIntention(CtrlIntention.AI_INTENTION_ATTACK, hater);
			return;
		}

		L2Attackable npc = (L2Attackable) _actor;

		final int range = Config.MAX_DRIFT_RANGE;
		if (npc instanceof FuzeGuardInstance) { // 是不是个守卫？
			// 命令L2GuardInstance返回自家因为没有目标要攻击
			((FuzeGuardInstance) npc).returnHome();
		} else if (npc instanceof L2MinionInstance) {// 仆从怪物跟随头领怪物
			FuzeMonsterInstance mob = ((L2MinionInstance) npc).getLeader();
			if (mob != null) {
				int offset = Util.FastDistance2D(npc.getX(), npc.getY(), mob.getX(), mob.getY());
				if (offset > 100) {//如果仆人和头领距离大于设定的值，仆人要走到头领周围
					setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new L2CharPosition(mob.getX() + Rnd.get(-range, range), mob.getY() + Rnd.get(-range, range)));
					return;
				}
			}
		} else if (!npc.isNoRndWalk() && Rnd.nextInt(RANDOM_WALK_RATE) == 0) {//命令怪物有
			L2Spawn spawn = npc.getSpawn();
			if (spawn != null) {
				int x1, y1;

				Scene scene = npc.getPosition().getScene();
				//如果NPC的出生点是特定的（0，0），那么随机整个地图的一个点
				if (spawn.getLocx() == 0 && spawn.getLocy() == 0) {
					// Calculate a destination point in the spawn area

					x1 = Rnd.nextInt(scene.getWidth());
					y1 = Rnd.nextInt(scene.getHeight());

				} else {
					x1 = spawn.getLocx();
					y1 = spawn.getLocy();

					//在范围内就随机一个点走动
					x1 += Rnd.get(-range, range);
					y1 += Rnd.get(-range, range);
				}

				//移动到x1，y1
				setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new L2CharPosition(x1, y1));

			}
		}
	}

	private L2Character thinkAttackable() {

		//每3s检查一次仇恨范围
		if (++_globalAggro < GLOBA_AGGRO_INTERVAL)
			return null;
		_globalAggro = 0;

		//如果混乱了不能攻击任何目标
		if (_actor.isConfused())
			return _attackTarget;

		L2Attackable npc = (L2Attackable) _actor;

		//主动性怪物，把玩家主动加入到仇恨列表中
		if (_selfAnalysis.aiType == AIType.ACTIVE) {
			FastMap<Long, L2Character> charactors = npc.getKnownList().getKnownCharacters();
			for (Entry<Long, L2Character> e = charactors.head(), n = charactors.tail(); (e = e.getNext()) != n;) {// 对每一个L2Character做检查
				L2Character target = e.getValue();
				if (autoAttackCondition(target)) {//检查是否能够主动的进攻
					// 从怪物的仇恨列表中获得此目标的仇恨值
					int hating = npc.getHating(target);

					// 如果角色不在仇恨列表中，把此角色加入到怪物的仇恨列表中，伤害加0，仇恨加0
					if (hating == 0) {
						npc.addDamageHate(target, 0, 0);
					}
				}
			}
		}

		L2Character hated = npc.getMostHated();//选择最仇恨的目标
		return hated;
	}

	/**
	 * 管理一个L2Attackable的AI攻击思考(被onEvtThink事件调用).<BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Update the attack timeout if actor is running</li> <li>If target is dead or timeout is expired, stop this attack and set the Intention to AI_INTENTION_ACTIVE</li> <li>Call all L2Object of its Faction inside the Faction Range</li> <li>Chose a target and order to attack it with magic skill or physical attack</li><BR>
	 * <BR>
	 * 
	 * 
	 */
	private void thinkAttack() {

		L2Character hater = _attackTarget;
		int cur_tick = GameTimeController.getGameTicks();

		//角色丢失或死亡或攻击超时
		if (hater == null || checkTargetLostOrDead(hater) || _attackTimeout < cur_tick) {
			//设置成警戒状态
			setIntention(AI_INTENTION_ACTIVE);
			return;
		}

		//自身情况的分析器
		SelfAnalysis self = _selfAnalysis;

		// 获得必要的信息来选择是物理攻击还是魔法攻击
		_actor.setTarget(hater);

		int haterX = hater.getX();
		int haterY = hater.getY();

		// 计算出到最仇恨目标的距离
		int mostHateDis = Util.FastDistance2D(_actor.getX(), _actor.getY(), haterX, haterY);

		//获取物理攻击范围
		int phyAttRange = _actor.getPhysicalAttackRange();

		//检查是否禁言是否拥有大范围的技能和恢复技能
		if (self.hasLongRangeSkills && self.lastDebuffTick + CAST_SKILL_INTERVAL < cur_tick && !_actor.isMuted()) {
			self.lastDebuffTick = cur_tick;

			//释放debuff的时间是不是又到了？（每6s做一次check）
			int size = 0;
			if ((size = self.debuffSkills.size()) != 0) {
				L2Skill sk = self.debuffSkills.get(Rnd.get(size));
				if (!(_actor.isSkillDisabled(sk.getId()) || _actor.getCurrentMp() < sk.getMpConsume() || mostHateDis > sk.getCastRange())) {
					L2Object OldTarget = _actor.getTarget();
					_actor.setTarget(hater);
					_accessor.doCast(sk);
					_actor.setTarget(OldTarget);
					return;
				}
			}

			if ((size = self.generalSkills.size()) != 0) {
				L2Skill sk = self.generalSkills.get(Rnd.get(size));
				if (!(_actor.isSkillDisabled(sk.getId()) || _actor.getCurrentMp() < sk.getMpConsume() || mostHateDis > sk.getCastRange())) {
					L2Object OldTarget = _actor.getTarget();
					_actor.setTarget(hater);
					_accessor.doCast(sk);
					_actor.setTarget(OldTarget);
					return;
				}
			}
		}

		//检查是否怪物离目标超过的物理攻击的范围
		if (mostHateDis > phyAttRange) {
			//靠近
			setIntention(CtrlIntention.AI_INTENTION_FOLLOW, hater, phyAttRange);
		} else {
			//如果是靠的足够近可以开始物理攻击
			_accessor.doAttack(hater);
		}
	}

	protected void thinkFollow() {
		L2Character followTarget = (L2Character) _intentionArg0;
		if (checkTargetLostOrDead(followTarget)) {
			// 设置意图为AI_INTENTION_ACTIVE
			setIntention(AI_INTENTION_ACTIVE);
			return;
		}

		int dis = Util.FastDistance2D(_actor.getX(), _actor.getY(), followTarget.getX(), followTarget.getY());

		int _range = 0;
		if (_intentionArg1 != null)
			_range = (Integer) _intentionArg1;
		if (dis > _range) {//如果不在角色的跟随范围内就查找角色的路径
			L2Attackable actor = (L2Attackable) _actor;
			if (dis > actor.getAggroRange()) {//大于仇恨范围就放弃跟随
				setIntention(AI_INTENTION_ACTIVE);
				return;
			}
			if (_actor.isMovementDisabled())
				return;
			if (_actor.updatePosition()) {
				if (!_actor.moveToNextRoutePoint()) {
					moveToPawn(followTarget, _range);
				}
			}
		} else {
			setIntention(CtrlIntention.AI_INTENTION_ATTACK, followTarget);
		}

	}

	/**
	 * Manage AI thinking actions of a L2Attackable.<BR>
	 * <BR>
	 */
	@Override
	protected void onEvtThink() {
		//System.out.println("getIntention():" + getIntention());
		if (_actor.isAllSkillsDisabled())
			return;
		try {
			switch (_intention) {
			case AI_INTENTION_ACTIVE:
				thinkActive();
				break;
			case AI_INTENTION_ATTACK:
				thinkAttack();
				break;
			case AI_INTENTION_FOLLOW:
				thinkFollow();
				break;
			default:
				super.onEvtThink();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	protected void onEvtArrivedRevalidate() {
		L2Character hater = thinkAttackable();
		if (hater != null) {
			setIntention(CtrlIntention.AI_INTENTION_ATTACK, hater);
		}
	}

	/**
	 * 通知被攻击的事件.<BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Init the attack : Calculate the attack timeout, Set the _globalAggro to 0, Add the attacker to the actor _aggroList</li> <li>Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance</li> <li>Set the Intention to AI_INTENTION_ATTACK</li><BR>
	 * <BR>
	 * 
	 * @param attacker
	 *            The L2Character that attacks the actor
	 * 
	 */
	@Override
	protected void onEvtAttacked(L2Character attacker) {
		// Add the attacker to the _aggroList of the actor
		L2Attackable mob = (L2Attackable) _actor;
		if (!mob.isCoreAIDisabled()) {
			mob.addDamageHate(attacker, 0, 1);

			//Set the Intention to AI_INTENTION_ATTACK
			if (getIntention() != AI_INTENTION_ATTACK) {
				setIntention(CtrlIntention.AI_INTENTION_ATTACK, attacker);
			}
		}
	}

	/**
	 * 通知侵略事件.<BR>
	 * <BR>
	 * 
	 * <B><U> Actions</U> :</B><BR>
	 * <BR>
	 * <li>Add the target to the actor _aggroList or update hate if already present</li> <li>Set the actor Intention to AI_INTENTION_ATTACK (if actor is L2GuardInstance check if it isn't too far from its home location)</li><BR>
	 * <BR>
	 * 
	 * @param attacker
	 *            The L2Character that attacks
	 * @param aggro
	 *            The value of hate to add to the actor against the target
	 * 
	 */
	@Override
	protected void onEvtAggression(L2Character target, int aggro) {
		L2Attackable me = (L2Attackable) _actor;

		if (target != null) {
			// Add the target to the actor _aggroList or update hate if already present
			me.addDamageHate(target, 0, aggro);

			// Set the actor AI Intention to AI_INTENTION_ATTACK
			if (getIntention() != CtrlIntention.AI_INTENTION_ATTACK) {
				setIntention(CtrlIntention.AI_INTENTION_ATTACK, target);
			}
		}
	}

}
